# coding=UTF-8
import datetime
import json
import os
import time

import requests

from common.enums import Enums
from service import data_service
from utils import date_util, doc_util, json_util, read_config

data_k_path = os.path.join(read_config.data_path, 'common', 'datak')
day_all_k_path = os.path.join(data_k_path, 'daykAll')
week_k_path = os.path.join(data_k_path, 'weekk')
month_k_path = os.path.join(data_k_path, 'monthk')


# 日k
def day_k_size(stock_code, stock_name, stock_exchange, size):
    print("获取日K,股票编码" + stock_code + '其他信息' + stock_name)
    time_stamp = int(round(time.time() * 1000))
    path_url = 'http://66.push2his.eastmoney.com/api/qt/stock/kline/get?cb=jQuery1124015253095575263576_' + str(
        time_stamp) + '&secid=' + str(
        stock_exchange) + '.' + str(stock_code) + \
               '&ut=fa5fd1943c7b386f172d6893dbfba10b&fields1=f1%2Cf2%2Cf3%2Cf4%2Cf5' \
               '&fields2=f51%2Cf52%2Cf53%2Cf54%2Cf55%2Cf56%2Cf57%2Cf58&klt=101' \
               '&fqt=0&end=20500101&lmt=' + str(size) + '&_=' + str(
        time_stamp)
    resp = requests.get(path_url)
    resp_content = str(resp.content.decode('utf-8'))
    if resp_content == '':
        print("\033[32m$$$$$$$$$$$$$$$$$获取日K线地址" + path_url + "不能使用$$$$$$$$$$$$$$$\033[0m")
    days_info_str = resp_content.split('(')[1].split(')')[0]
    days_info_str = days_info_str.replace('null', '""')
    resp_json = json_util.to_json(days_info_str)
    if resp_json['data'] == '':
        print("code" + stock_code + "请求k线为空")
        return []
    klines = resp_json['data']['klines']
    if size >= 120 and size < 1000:  # 够数量才进行记录 超过数量不更新 去总的调度
        record_day_k_local(stock_code, klines)
    return klines


# 日k
def day_k(stock_code, stock_name, stock_exchange):
    return day_k_size(stock_code, stock_name, stock_exchange, 120)


# 线上日k
def day_k_online_up_cache(code, size):
    stock_info = data_service.stock(code)
    k_lines = day_k_size(code, stock_info['name'], stock_info['exchange'], size)
    day_k_format = format_k_lines(k_lines)
    day_k_cache[code] = [{'up_time': str(int(round(time.time() * 1000)))}] + day_k_format
    return day_k_format


# 实时日k 日期降序排列
def day_k_rt(code, size):
    name = data_service.stock(code)['name']
    exchange = data_service.stock(code)['exchange']
    return format_k_lines(day_k_size(code, name, exchange, size))


# 获取更新时间戳
def get_up_time():
    today = date_util.get_date_time()
    if today.hour >= 15:
        today = date_util.add_day(today, 1)
        today_str = date_util.date_to_str(today, '%Y-%m-%d')
        today_str += " 08:30:00"
        today = date_util.str_to_date(today_str, '%Y-%m-%d %H:%M:%S')
    while 1:
        if not date_util.valid_day(date_util.date_to_str(today, '%Y-%m-%d')):
            today = date_util.add_day(today, 1)
        else:
            break
    return str(round(time.mktime(today.timetuple())))


# 更新本地日k
# klines: 日期从大到小
def record_day_k_local(code, klines):
    today = date_util.get_date_time()
    dayk_file = open(os.path.join(read_config.data_path, 'common', 'dayk', code + '.txt'), mode='w', encoding='utf-8')
    stock_info = {}
    stock_info['up_time'] = get_up_time()  # 秒时间戳
    dayk_file.write(str(stock_info) + '\n')
    klines_l = len(klines)
    for i in range(klines_l):
        dayk_file.write(str(format_k_line(klines[klines_l - i - 1])) + '\n')
    dayk_file.close()


def format_k_lines(klines):
    k_lines = []
    klines_l = len(klines)
    for i in range(klines_l):
        k_lines.append(format_k_line(klines[klines_l - i - 1]))
    return k_lines


def format_k_line(kline):
    kline_arr = kline.split(',')
    day_k_info = {}
    day_k_info[Enums.Stock.rq.value] = kline_arr[0]
    day_k_info[Enums.Stock.kp.value] = kline_arr[1]
    day_k_info[Enums.Stock.sp.value] = kline_arr[2]
    day_k_info[Enums.Stock.max.value] = kline_arr[3]
    day_k_info[Enums.Stock.min.value] = kline_arr[4]
    day_k_info[Enums.Stock.cjl.value] = kline_arr[5]
    day_k_info[Enums.Stock.cje.value] = kline_arr[6]
    day_k_info[Enums.Stock.zf.value] = kline_arr[7]
    return day_k_info


# 更新本地日k
def update_day_k_local(stock_code):
    day_k_result = up_day_k_local(stock_code)
    return day_k_result[1:len(day_k_result)]


# 更新本地日k + 包含更新时间
def up_day_k_local(stock_code):
    day_k_result = []
    dayk_file = open(os.path.join(read_config.data_path, 'common', 'dayk', stock_code + '.txt'), mode='w',
                     encoding='utf-8')
    stock_info = {}
    stock_info['up_time'] = get_up_time()  # 秒时间戳
    day_k_result.append(stock_info)  # 新增的时间*******
    dayk_file.write(str(stock_info).replace('\'', '\"') + '\n')
    dict = data_service.stock(stock_code)
    klines = day_k(stock_code, dict['name'], dict['exchange'])
    # ['2020-01-13,6.70,6.93,6.93,6.67,54385,37161012.00,3.86', '2020-01-14,6.93,7.28,7.28,6.87,92968,66365371.00,5.92']
    klines_l = len(klines)
    for i in range(klines_l):
        day_k_result.append(format_k_line(klines[klines_l - i - 1]))
        dayk_file.write(str(format_k_line(klines[klines_l - i - 1])) + '\n')
    dayk_file.close()
    return day_k_result


# 日k-逆序（日期从大到小）k线一小时更新一次
# day_k_result = [{},{}]
def day_k_local(stock_code, size):
    return get_day_k_cache_frequency(stock_code, 3600)[1:size + 1]


# 日k-逆序（日期从大到小）k线一小时更新一次
# day_k_result = [{},{}]
def day_k_all_local(stock_code):
    dayk_local = get_day_k_cache_frequency(stock_code, 3600)
    dayk_len = len(dayk_local)
    return dayk_local[1:dayk_len]


# 日k-逆序（日期从大到小）k线一小时更新一次
# day_k_result = [{},{}]
def day_k_local_frequency(code, size, seconds):
    return get_day_k_cache_frequency(code, seconds)[1:size + 1]


# 获取本地文档中day_k
# seconds 更新频率
def day_k_doc_frequency(code, seconds):
    file_path = os.path.join(read_config.data_path, 'common', 'dayk', code + '.txt')
    if not os.path.exists(file_path):
        open(file_path, mode='w', encoding='utf-8').close()
    day_k_result = doc_util.get_path_doc_info(file_path)
    if len(day_k_result) < 2:
        day_k_result = up_day_k_local(code)
    else:
        first_line = json_util.info_to_json(day_k_result[0])
        up_timestamp = int(first_line['up_time'])
        td_timestamp = date_util.get_timestamp_now()
        if (td_timestamp - up_timestamp) > seconds:
            day_k_result = up_day_k_local(code)
    return day_k_result


# 获取缓存中的day_k 缓存的是全部的本地
# seconds 更新频率
def get_day_k_cache_frequency(code, seconds):
    if code not in day_k_cache.keys():
        day_k_cache[code] = day_k_doc_frequency(code, seconds)
    else:
        day_ks = json_util.info_to_json(day_k_cache[code])
        first_line = json_util.info_to_json(day_ks[0])
        up_timestamp = int(first_line['up_time'])
        td_timestamp = date_util.get_timestamp_now()
        if (td_timestamp - up_timestamp) > seconds:
            day_k_cache[code] = day_k_doc_frequency(code, seconds)
    return day_k_cache[code]


# 获取缓存中的day_k
def get_day_k_cache(code):
    return get_day_k_cache_frequency(code, 3600)


# day_k_result 逆序 时间倒叙
# 不包含当天的k线 获取最新的几条数据
def day_k_local_previous(code, size):
    return get_day_k_cache(code)[2:size + 2]


# 日k-逆序（日期从大到小）
# day_k_result = [{},{}]
def day_k_just_local(stock_code, size):
    file_path = os.path.join(read_config.data_path, 'common', 'dayk', stock_code + '.txt')
    if not os.path.exists(file_path):
        open(file_path, mode='w', encoding='utf-8').close()
    dayk_file = open(file_path, mode='r', encoding='utf-8')
    day_k_result = []
    one_day = dayk_file.readline()
    if not one_day:
        dayk_file.close()
        day_k_result = update_day_k_local(stock_code)
    else:
        one_day_k = json.loads(one_day[:-1])
        last_time = int(one_day_k['up_time'])
        # 是最新的日期
        if (((round(time.time())) - last_time) > 0) and \
                (datetime.datetime.day != datetime.datetime.fromtimestamp(last_time).day):
            day_k_result = update_day_k_local(stock_code)
        else:
            for i in range(size):
                line = dayk_file.readline()[:-1]
                if line == '':
                    break
                day_k_result.append(line)
    dayk_file.close()
    return day_k_result


def get_day_k(stock_code):
    dict = data_service.stock(stock_code)
    return day_k(stock_code, dict['name'], dict['exchange'])


# 判断是否涨停
def judge_harden(after_sp, before_sp):
    if (after_sp < before_sp):
        return 0
    if ((after_sp - before_sp) / before_sp < 0.098):
        return 0
    return 1


# 获取周K
# 返回数据: 日期从大到小
def week_k_size(code, size):
    result_week_k = []
    new_val_date = date_util.find_valid_day(date_util.get_date_str(), -1)
    if os.path.exists(week_k_path + code):
        week_ks = doc_util.get_path_doc_info(os.path.join(week_k_path, code))  # 日期从小到大 正序
        up_rq = json_util.info_to_json(week_ks[len(week_ks) - 1])['rq']
        if up_rq == new_val_date:
            week_len = len(week_ks)
            if week_len < size:
                size = week_len
            week_ks.reverse()  # 日期从大到小 倒序
            result_week_k = week_ks[0:size]
        else:
            result_week_k = up_the_k(week_ks, code, up_rq, new_val_date, size, 5)
    else:
        week_ks = gen_result_ks(code, new_val_date, 5)  # 日期从小到大
        week_ks.reverse()
        week_len = len(week_ks)
        if week_len < size:
            size = week_len
        result_week_k = week_ks[0:size]
    return result_week_k


# 获取月K
# k 结果 时间逆序 最新的在最前端
def month_k_size(code, size):
    result_month_k = []
    valid_day = date_util.find_valid_day(date_util.get_date_str(), -1)
    if os.path.exists(month_k_path + code):
        month_ks = doc_util.get_path_doc_info(month_k_path + code)
        up_rq = json_util.info_to_json(month_ks[0])['up_rq']
        if up_rq == valid_day:
            month_len = len(month_ks)
            if month_len < size:
                size = month_len
            month_ks.reverse()
            result_month_k = month_len[0:size]
        else:
            result_month_k = up_the_k(month_ks, code, up_rq, valid_day, size, 30)
    else:
        month_ks = gen_result_ks(code, valid_day, 30)  # 日期从小到大
        month_ks.reverse()
        month_len = len(month_ks)
        if month_len < size:
            size = month_len
        result_month_k = month_ks[0:size]
    return result_month_k


# 生成ks
# 返回数据:日期从小到大
# flag: 只支持周和月
def gen_result_ks(code, valid_date, flag):
    day_all_ks = []
    if not os.path.exists(os.path.join(day_all_k_path, code)):
        day_all_ks += format_k_lines(
            day_k_size(code, data_service.stock(code)['name'], data_service.stock(code)['exchange'], 12000))
        day_all_ks.reverse()  # day_all_ks 时间从小到大
        if len(day_all_ks) == 0:
            return []
        doc_util.gen_new_doc(day_all_k_path, code, day_all_ks)
    day_all_ks = doc_util.get_path_doc_info(os.path.join(day_all_k_path, code))  # day_all_ks 时间从小到大
    if len(day_all_ks) == 0 :return []
    day_up_rq = json_util.info_to_json(day_all_ks[len(day_all_ks) - 1])['rq']
    if day_up_rq != valid_date:
        day_all_ks = up_day_k_all(day_all_ks, code, day_up_rq, valid_date)
    day_all_ks.reverse()
    result_ks = gen_the_k(code, day_all_ks, [], flag)  # result_ks 生成需要的k 日期从大到小
    result_path = ''
    if flag == 5:
        result_path = week_k_path
    elif flag == 30:
        result_path = month_k_path
    result_ks.reverse()
    doc_util.gen_new_doc(result_path, code, result_ks)
    return result_ks


# 更新所有的 k
# flag: 5:周,30:月 不支持天的更新
# old_ks 日期顺序(存储在本地的日期顺序从小到大)
# 返回数据顺序: result_ks 日期从大到小
def up_the_k(old_ks, code, up_rq, valid_day, size, flag):
    diff_days = date_util.diff_days_str(up_rq, valid_day)
    if diff_days <= 0:
        return old_ks
    day_ks_all = get_day_k_all(code)  # 日期从小到大
    ks_all_len = len(day_ks_all)
    add_flag = 0
    for i in range((ks_all_len - diff_days - 2), ks_all_len):
        k_info = json_util.to_json(day_ks_all[i])
        if add_flag == 0 and up_rq == k_info['rq']:
            old_ks[len(old_ks) - 1] = k_info
        elif add_flag == 1:
            old_ks.append(k_info)
    doc_util.gen_new_doc(day_all_k_path, code, old_ks)
    if size > len(day_ks_all):
        size = len(day_ks_all)
    old_ks.reverse()
    return old_ks[0:size]


# 生成需要的k数据
# day_all_ks: 日期倒序从大到小
# 返回数据:日期倒序从大到小
# flag :5 周k 30:月k
def gen_the_k(code, day_all_ks, result_ks, flag):
    day_all_len = len(day_all_ks)
    start_flag = 1
    k_info = {"rq": "", "kp": 0, "sp": 0, "max": 0, "min": 999999}
    begin_rq = json_util.info_to_json(day_all_ks[day_all_len - 1])['rq']
    for i in range(day_all_len):
        dayk = json_util.info_to_json(day_all_ks[i])
        rq = dayk['rq']
        if start_flag:  # 新开始的一个周期
            k_info['sp'] = float(dayk['sp'])
            k_info['rq'] = dayk['rq']
            start_flag = 0
        trigger_flag = 0
        if flag == 5:
            flag_num = date_util.isoweekdaystr(rq)
            last_day_rq = k_info['rq']
            last_week_num = date_util.isoweekdaystr(k_info['rq'])
            diff_days = date_util.diff_days_str(rq, last_day_rq)
            if (diff_days > last_week_num and flag == 5):  # 超过了当前星期
                trigger_flag = 1
        elif flag == 30:
            flag_num = int(rq[-2:])
            last_day_rq = k_info['rq']
            last_month = last_day_rq[5:7]
            this_month = rq[5:7]
            if last_month != this_month:
                trigger_flag = 1
        else:
            return []
        if trigger_flag == 1:
            print(code + '超过了一个周期' + last_day_rq)
            # 进行上一周的封顶 和 本周的开始
            last_day_k = json_util.info_to_json(day_all_ks[i - 1])
            k_info['kp'] = float(last_day_k['kp'])
            result_ks.append(k_info)  # 添加在队列
            k_info = {"rq": "", "kp": 0, "sp": 0, "max": 0, "min": 999999}
            k_info['sp'] = float(dayk['sp'])
            k_info['rq'] = dayk['rq']
            k_info['max'] = float(dayk['max'])
            k_info['min'] = float(dayk['min'])
            continue
        max = float(dayk['max'])
        min = float(dayk['min'])
        if k_info['max'] < max:
            k_info['max'] = max
        if k_info['min'] > min:
            k_info['min'] = min
        if flag_num == 1 or rq == begin_rq:
            k_info['kp'] = float(dayk['kp'])
            result_ks.append(k_info)
            k_info = {"rq": "", "kp": 0, "sp": 0, "max": 0, "min": 999999}
            start_flag = 1  # 一个周期的结束
    return result_ks


# 更新所有的dayk
# day_all_ks 时间从小到大
# 返回数据: 时间从小到大
def up_day_k_all(day_all_ks, code, day_up_rq, valid_date):
    diff_days = date_util.diff_days_str(day_up_rq, valid_date)
    # 更新 dayall
    q_size = (int(diff_days / 7) + 1) * 5
    day_ks = day_k_local(code, q_size)  # todo 长度不足 后期异常优化
    day_ks.reverse()  # 日期变换为从小到大
    add_flag = 0
    for day_k in day_ks:
        day_k = json_util.info_to_json(day_k)
        if add_flag == 1:
            day_all_ks.append(day_k)
            continue
        rq = day_k['rq']
        if rq == day_up_rq:
            day_all_ks[len(day_all_ks) - 1] = day_k
            add_flag = 1
    doc_util.gen_new_doc(day_all_k_path, code, day_all_ks)
    return day_all_ks


# 实时的k线
def day_k_cache_rt(code, size, rt_price):
    result_list = day_k_local_frequency(code, size, 3600)
    k_info = json_util.info_to_json(result_list[0])
    k_info['sp'] = rt_price
    result_list[0] = k_info
    return result_list


# 获取缓存中的day_k
# 返回数据:日期从小到大 正序
def get_day_k_all(code):
    if code not in day_k_all_cache.keys():
        ks = doc_util.get_path_doc_info(os.path.join(day_all_k_path, code))
        ks_len = len(ks)
        if ks_len == 0:
            ks = gen_result_ks(code, date_util.get_date_str(), 1)  # 日期从小到大
        set_d_kac(code, ks)
    day_ks_all = json_util.to_json(day_k_all_cache[code])
    day_k0 = json_util.to_json(day_ks_all[len(day_ks_all) - 1])
    valid_day = date_util.find_valid_day(date_util.get_date_str(), -1)
    if day_k0['rq'] != valid_day:
        set_d_kac(code, up_day_k_all(day_k_all_cache[code], code, day_k0['rq'], valid_day))
    return day_k_all_cache[code]


# 含有缓冲更新日K(对于内存无限制过大时需要优化)
day_k_cache = {'000000': {"total": 0, 'del_flag': 2, 'keys1': [], 'keys2': []}}
day_k_all_cache = {'000000': {"total": 0, 'del_flag': 2, 'keys1': [], 'keys2': []}}


def set_d_kc(code, data):
    cache_info = day_k_cache['000000']
    total = cache_info['total']
    self_check = 0
    size = 10
    if total >= size:
        del_flag = cache_info['del_flag']
        if del_flag == 1:
            del_list = cache_info['keys1']
            cache_info['keys1'] = []
            cache_info['del_flag'] = 2
        else:
            del_list = cache_info['keys2']
            cache_info['keys2'] = []
            cache_info['del_flag'] = 1
        for key in del_list:
            if key in day_k_cache.keys():
                day_k_cache.pop(key)
            else:
                self_check = 1
        cache_info['total'] = int(size / 2)
    if cache_info['del_flag'] == 1:
        keys2 = cache_info['keys2']
        if len(keys2) >= int(size / 2):
            keys1 = cache_info['keys1']
            keys1.append(code)
            cache_info['del_flag'] = 2
        else:
            keys2.append(code)
        cache_info['total'] += 1
    else:
        keys1 = cache_info['keys1']
        if len(keys1) >= int(size / 2):
            keys2 = cache_info['keys2']
            keys2.append(code)
            cache_info['del_flag'] = 1
        else:
            keys1.append(code)
        cache_info['total'] += 1
    day_k_cache['000000'] = cache_info
    day_k_cache[code] = data
    if self_check == 1:
        print("day_k_cache:缓存自校验")
        keys = cache_info['keys1'] + cache_info['keys2']
        self_del_list = []
        for k in day_k_cache.keys():
            if k != '000000' and (k not in keys):
                self_del_list.append(k)
        for k in self_del_list:
            day_k_cache.pop(k)


def set_d_kac(code, data):
    cache_info = day_k_all_cache['000000']
    total = cache_info['total']
    self_check = 0
    size = 10
    if total >= size:
        del_flag = cache_info['del_flag']
        if del_flag == 1:
            del_list = cache_info['keys1']
            cache_info['keys1'] = []
            cache_info['del_flag'] = 2
        else:
            del_list = cache_info['keys2']
            cache_info['keys2'] = []
            cache_info['del_flag'] = 1
        for key in del_list:
            if key in day_k_all_cache.keys():
                day_k_all_cache.pop(key)
            else:
                self_check = 1
        cache_info['total'] = int(size / 2)
    if cache_info['del_flag'] == 1:
        keys2 = cache_info['keys2']
        if len(keys2) >= int(size / 2):
            keys1 = cache_info['keys1']
            keys1.append(code)
            cache_info['del_flag'] = 2
        else:
            keys2.append(code)
        cache_info['total'] += 1
    else:
        keys1 = cache_info['keys1']
        if len(keys1) >= int(size / 2):
            keys2 = cache_info['keys2']
            keys2.append(code)
            cache_info['del_flag'] = 1
        else:
            keys1.append(code)
        cache_info['total'] += 1
    day_k_all_cache['000000'] = cache_info
    day_k_all_cache[code] = data
    if self_check == 1:
        print("day_k_all_cache:缓存自校验")
        keys = cache_info['keys1'] + cache_info['keys2']
        self_del_list = []
        for k in day_k_all_cache.keys():
            if k != '000000' and (k not in keys):
                self_del_list.append(k)
        for k in self_del_list:
            day_k_all_cache.pop(k)


# beg_rq: 比较早的日期
# 返回列表: 日期顺序 从小到大顺序排列
def day_ks_gap(code, beg_rq, end_rq):
    result_ks = []
    ks = get_day_k_all(code)  # 日期从小到大顺序排列
    ks_len = len(ks)
    old_rq = json_util.to_json(ks[0])['rq']
    if date_util.diff_days_str(old_rq, beg_rq) < 0:
        beg_rq = old_rq
    add_flag = 0
    for i in range(ks_len - 1):
        if add_flag == 0 and date_util.diff_days_str(beg_rq, json_util.to_json(ks[i])['rq']) >= 0:
            add_flag = 1
            result_ks.append(json_util.to_json(ks[i]))
            if beg_rq == end_rq:
                return result_ks
        elif add_flag == 1:
            dif_days = date_util.diff_days_str(end_rq, json_util.to_json(ks[i])['rq'])
            if dif_days > 0:
                return result_ks
            result_ks.append(json_util.to_json(ks[i]))
            if dif_days == 0:
                return result_ks
    return result_ks


# 获取指定日期月K
# k 结果 时间逆序 最新的在最前端 日期从大到小
def month_ks_gp(code, rq, size):
    month_ks = month_k_size(code, size)
    if rq == json_util.to_json(month_ks[0])['rq']:
        return month_ks
    else:
        day_ks = day_ks_gap(code, date_util.add_day_str(rq, -40), rq)
        day_ks.reverse()  # 转换成日期从大到小
        gp_month_ks = gen_the_k(code, day_ks, [], 30)
        month_ks[0] = gp_month_ks[0]
    return month_ks


if __name__ == '__main__':
    # week = month_k_size('600031', 10)
    # for stock in week:
    #     print(stock)
    # day_k_result = day_k_local_previous('603738', 5)
    # day_k_result = day_ks_gap('603738', '2020-12-01', '2020-12-03')
    # day_k_result = get_day_k_all('603738')
    # day_k_result = month_k_size('603738', 10)
    # k_result = month_ks_gp('603738', date_util.get_date_str(), 10)
    # dk_result = day_ks_gap('603738', '2020-09-30', '2021-01-29')
    dks = day_k_size('601206', '海尔施', '1', 12000)
    print(dks)
